import MediaDeviceDao from '../dao/MediaDeviceDao';
import { IPermissionName } from '../interface';
import EventEmit from '../Event';
import { log } from '../aop/log';

class MediaStreamController {
	/**
	 * 验证用户授权状态
	 * @param { IPermissionName } permissionName
	 * @returns Promise<IPermissionName>
	 */
	private async validatePermissionWithPermissionName(permissionName: IPermissionName) {
		// 'microphone', 'camera'
		const { state } = await navigator.permissions.query({
			name: permissionName as PermissionName
		});
		return { state, name: permissionName };
	}

	/**
	 * 初始化 MediaStreamController
	 */
	public init = async () => {
		try {
			const data = await this.validateMicAndCameraPermission();
			if (data?.microphone.state === 'granted' && data.camera.state === 'granted') {
				await this.getUserMediaDevices();
			}
		} catch (error) {
			console.error('init media stream controller error', error);
		} finally {
			this.initDeviceEvent();
		}
	};

	/**
	 * 初始化设备监听
	 */
	private initDeviceEvent() {
		navigator.mediaDevices.ondevicechange = async event => {
			await this.getUserMediaDevices();
			this.notifyDeviceChange();
		};
	}

	/**
	 * 检测设备事件
	 * @returns
	 */
	private notifyDeviceChange() {
		if (MediaDeviceDao.allMediaDevicesMap.size === MediaDeviceDao.validMediaDevicesMap.size) return;

		if (MediaDeviceDao.allMediaDevicesMap.size > MediaDeviceDao.validMediaDevicesMap.size) {
			MediaDeviceDao.allMediaDevicesMap.forEach((value, key) => {
				if (!MediaDeviceDao.validMediaDevicesMap.has(key)) {
					MediaDeviceDao.allMediaDevicesMap.delete(key);
					if (MediaDeviceDao.audioInputDeviceId === value.deviceId && value.kind === 'audioinput') {
						MediaDeviceDao.setAudioInputDeviceId(undefined);
					}
					if (MediaDeviceDao.videoInputDeviceId === value.deviceId && value.kind === 'videoinput') {
						MediaDeviceDao.setVideoInputDeviceId(undefined);
					}
					if (MediaDeviceDao.audioOutputDeviceId === value.deviceId && value.kind === 'audiooutput') {
						MediaDeviceDao.setAudioOutputDeviceId(undefined);
					}
					EventEmit.setDeviceEventLinstener({ event: value.kind, type: 'remove' }, value);
				}
			});
		} else {
			MediaDeviceDao.validMediaDevicesMap.forEach((value, key) => {
				if (!MediaDeviceDao.allMediaDevicesMap.has(key)) {
					MediaDeviceDao.allMediaDevicesMap.set(key, value);
					// Message.normal(`设备 ${value.label} 已插入`);
					if (MediaDeviceDao.audioInputDeviceId === value.deviceId && value.kind === 'audioinput') {
						MediaDeviceDao.setAudioInputDeviceId(value.deviceId);
					}
					if (MediaDeviceDao.videoInputDeviceId === value.deviceId && value.kind === 'videoinput') {
						MediaDeviceDao.setVideoInputDeviceId(value.deviceId);
					}
					if (MediaDeviceDao.audioOutputDeviceId === value.deviceId && value.kind === 'audiooutput') {
						MediaDeviceDao.setAudioOutputDeviceId(value.deviceId);
					}
					EventEmit.setDeviceEventLinstener({ event: value.kind, type: 'add' }, value);
				}
			});
		}

		EventEmit.setDeviceEventLinstener({ event: 'deviceChange', type: 'change' }, [...MediaDeviceDao.validMediaDevicesMap.values()]);
	}

	/**
	 * 验证麦克风、摄像头权限
	 * @returns
	 */
	public async validateMicAndCameraPermission() {
		try {
			const microphone = await this.validatePermissionWithPermissionName('microphone');
			const camera = await this.validatePermissionWithPermissionName('camera');

			// 已授权
			return { microphone, camera };
		} catch (error) {
			await this.testMicAndCamera();
			console.error('validate Permission error:', error);
		}
	}

	/**
	 * 获取用户所有设备信息
	 */
	public async getUserMediaDevices() {
		try {
			const devices = await navigator.mediaDevices.enumerateDevices();
			MediaDeviceDao.setAllMediaDevices(devices);
			console.log('MediaDeviceDao', MediaDeviceDao.videoInput, devices);
		} catch (error) {
			console.error('get user media devices error:', error);
		}
	}

	/**
	 * 验证设备是否有效
	 * @returns
	 */
	public async validateDevicesSupport() {
		try {
			await this.testMicAndCamera();
			let audio = MediaDeviceDao.audioInput.length !== 0;
			let video = MediaDeviceDao.videoInput.length !== 0;
			return { audio, video };
		} catch (error) {
			return { audio: false, video: false };
		}
	}

	/**
	 * 测试麦克风、摄像头可用性
	 */
	public async testMicAndCamera() {
		let constraints = {
			audio: true,
			video: true
		};
		try {
			const stream = await navigator.mediaDevices.getUserMedia(constraints);
			if (stream) {
				await this.getUserMediaDevices();
				let tracks = stream.getTracks();
				for (let track of tracks) {
					track.stop();
				}
				console.log('getUserMediaPermission success');
			}
		} catch (err) {
			console.log('getUserMediaPermission error', err);
		}
	}

	public testMic() {
		return new Promise((resolve, reject) => {
			navigator.mediaDevices
				.getUserMedia({ audio: true, video: false })
				.then(async stream => {
					await this.getUserMediaDevices();
					let tracks = stream.getTracks();
					for (let track of tracks) {
						track.stop();
					}
					resolve('ok');
				})
				.catch(e => {
					console.error('test mic failed', e);
					reject('failed');
				});
		});
	}

	public testCamera() {
		return new Promise((resolve, reject) => {
			navigator.mediaDevices
				.getUserMedia({ audio: false, video: true })
				.then(async stream => {
					await this.getUserMediaDevices();
					let tracks = stream.getTracks();
					for (let track of tracks) {
						track.stop();
					}
					resolve('ok');
				})
				.catch(e => {
					console.error('test camera failed', e);
					reject('failed');
				});
		});
	}

	@log('日志消息')
	public async createLocalStream(constraints: MediaStreamConstraints) {
		try {
			const stream = await navigator.mediaDevices.getUserMedia(constraints);
			MediaDeviceDao.setLocalStream(stream);
			return stream;
		} catch (error) {
			console.log('create local stream failed', error);
			return error;
		}
	}

	@log('日志消息')
	public async stopLocal() {
		if (MediaDeviceDao.localStream) {
			const traks = MediaDeviceDao.localStream?.getTracks();
			traks?.map(track => track.stop());
			return 'success';
		}
	}

	/**
	 * 设置默认的设备ID
	 */
	public setDefaultMediaDeviceID() {
		let audioInputDevice = MediaDeviceDao.audioInput.find(item => MediaDeviceDao.communicationsMap.has(item.kind) && MediaDeviceDao.communicationsMap.get(item.kind)!.groupId === item.groupId);
		let audioOutputDevice = MediaDeviceDao.audioOutput.find(item => MediaDeviceDao.communicationsMap.has(item.kind) && MediaDeviceDao.communicationsMap.get(item.kind)!.groupId === item.groupId);
		let videoInputDevice = MediaDeviceDao.videoInput[0]?.deviceId || undefined;
		if (!MediaDeviceDao.audioInputDeviceId) {
			if (audioInputDevice) {
				MediaDeviceDao.setAudioInputDeviceId(audioInputDevice.deviceId ? audioInputDevice.deviceId : undefined);
			} else {
				MediaDeviceDao.setAudioInputDeviceId(undefined);
			}
		}

		if (!MediaDeviceDao.audioOutputDeviceId) {
			if (audioOutputDevice) {
				MediaDeviceDao.setAudioOutputDeviceId(audioOutputDevice.deviceId);
			} else {
				let audioOutputDevice = MediaDeviceDao.audioOutput.find(item => MediaDeviceDao.audioInput.map(k => k.groupId === item.groupId));
				MediaDeviceDao.setAudioOutputDeviceId(audioOutputDevice ? audioOutputDevice.deviceId : undefined);
			}
		}

		if (!MediaDeviceDao.videoInputDeviceId) {
			MediaDeviceDao.setVideoInputDeviceId(videoInputDevice);
		}

		console.log('setDefaultMediaDeviceID:', MediaDeviceDao.audioInputDeviceId, MediaDeviceDao.videoInputDeviceId, MediaDeviceDao.audioOutputDeviceId);
	}

	@log('日志消息')
	public replaceVideoStream(videoId: string) {
		try {
			let video = document.getElementById(videoId) as HTMLVideoElement;
			if (video) {
				video.srcObject = MediaDeviceDao.localStream;
				video.play();
				return videoId;
			}
		} catch (error) {
			console.error('replaceVideoStream error:', error);
			return `replace stream error: ${JSON.stringify(error)}`;
		}
	}

	@log('日志消息')
	public async switchSpeaker(videoId: string, deviceId: string) {
		try {
			let id = videoId || '';
			let video = document.getElementById(id) as HTMLMediaElement;
			console.log(video, deviceId);

			if (video) {
				//@ts-ignore
				await video.setSinkId(deviceId);
				// @ts-ignore
				return `${video.sinkId}`;
			}
		} catch (error) {
			console.error('switch speaker error:', error);
			return `switch speaker error: ${JSON.stringify(error)}`;
		}
	}

	public getVideoInputDeviceID() {
		return MediaDeviceDao.videoInputDeviceId;
	}

	public getAudioInputDeviceID() {
		return MediaDeviceDao.audioInputDeviceId;
	}

	public getDefaultAudioInputDeviceID() {
		let audioInput = MediaDeviceDao.audioInput.find(item => MediaDeviceDao.communicationsMap.has(item.kind) && MediaDeviceDao.communicationsMap.get(item.kind)!.groupId === item.groupId);
		return audioInput?.deviceId;
	}

	public getSpeakerDeviceID() {
		return MediaDeviceDao.audioOutputDeviceId;
	}

	public getDefaultSpeaker() {
		let audioOutputDevice = MediaDeviceDao.audioOutput.find(item => MediaDeviceDao.communicationsMap.has(item.kind) && MediaDeviceDao.communicationsMap.get(item.kind)!.groupId === item.groupId);
		return audioOutputDevice?.deviceId;
	}

	public setAudioInputDeviceId(deviceID: string | undefined) {
		return MediaDeviceDao.setAudioInputDeviceId(deviceID);
	}

	public setVideoInputDeviceId(deviceID: string | undefined) {
		return MediaDeviceDao.setVideoInputDeviceId(deviceID);
	}

	public setAudioOutputDeviceId(deviceID: string | undefined) {
		return MediaDeviceDao.setAudioOutputDeviceId(deviceID);
	}

	public getAudioDevices() {
		return MediaDeviceDao.audioInput;
	}

	public getVideoDevices() {
		return MediaDeviceDao.videoInput;
	}

	public getSpeakerDevices() {
		return MediaDeviceDao.audioOutput;
	}

	public findDeviceWithDevicID(deviceID: string) {
		return MediaDeviceDao.findDeviceWithDevicID(deviceID);
	}

	/**
	 * 从流中，设置默认设备ID
	 * @param stream
	 */
	public async setInputDefaultDeviceIDWithSteam(stream: MediaStream) {
		const audioTrack = stream.getAudioTracks();
		const videoTrack = stream.getVideoTracks();
		for (let track of audioTrack) {
			if (track.getSettings().deviceId && MediaDeviceDao.validateDeviceID(track.getSettings().deviceId!)) {
				if (!MediaDeviceDao.audioInputDeviceId) {
					this.setAudioInputDeviceId(track.getSettings().deviceId);
				}
			}
		}

		for (let track of videoTrack) {
			if (track.getSettings().deviceId && MediaDeviceDao.validateDeviceID(track.getSettings().deviceId!)) {
				if (!MediaDeviceDao.videoInputDeviceId) {
					this.setVideoInputDeviceId(track.getSettings().deviceId);
				}
			}
		}

		if (MediaDeviceDao.validMediaDevicesMap.size === 0) {
			await this.getUserMediaDevices();
		}
	}
}

export default MediaStreamController;
